package com.icee.myth.deamon;

import com.google.protobuf.ByteString;
import com.icee.myth.deamon.channelHandler.DeamonToManagerHandler;
import com.icee.myth.deamon.encoder.DeamonToManagerEncoder;
import com.icee.myth.deamon.message.serverMessage.Message;
import com.icee.myth.deamon.message.serverMessage.ServerConfigDataDeamonMessage;
import com.icee.myth.deamon.message.serverMessage.StartServerDeamonMessage;
import com.icee.myth.deamon.message.serverMessage.builder.DeamonMessageBuilder;
import com.icee.myth.deamon.messageQueue.ServerMessageQueue;
import com.icee.myth.deamon.processOutputThread.ProcessOutputThread;
import com.icee.myth.deamon.utils.LinkedTransferQueue;
import com.icee.myth.deamon.utils.LogConsts;
import com.icee.myth.deamon.utils.MLogger;
import com.icee.myth.deamon.utils.StackTraceUtil;
import com.icee.myth.utils.Version;
import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Timer;
import java.util.TimerTask;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import static com.icee.myth.deamon.utils.Consts.HEARTBEAT_PERIOD;

/**
 * 守护进程
 * @author liuxianke
 */
public class Deamon implements Runnable {

    // 私有无参构造函数
    private Deamon() { }
    /** Singleton，没有多线程同步的需要，只是方便全局访问 */
    private static Deamon INSTANCE = new Deamon();
    /** getInstance操作的第一次调用在main中，因此无需同步 */
    public static Deamon getInstance() {
        return INSTANCE;
    }

    /** 全局共享的消息队列 */
    private final LinkedTransferQueue<Message> messageQueue = ServerMessageQueue.queue();

    /** Manager Client */
    private DeamonToManagerClient deamonToManagerClient;
    /** Manager服务地址 */
    private ServerAddress managerAddress;
    /** bootstrap端口 */
    private int managerBootstrapPort;

    /** 操作系统名 */
    private String osName;
    /** 心跳计时器 */
    private Timer heartbeatTimer;

    /* Deamon启动参数说明：
完整参数
java -ea -Dfile.encoding=UTF-8 -classpath dist\ProjectKManagerDeamon.jar com.icee.myth.deamon.Deamon -managerHost 192.168.100.209 -managerPort 4768 -bootstrapPort 4769 -osName windows

1. 不需要变化的参数
java -ea -Dfile.encoding=UTF-8 -classpath dist\ProjectKManagerDeamon.jar com.icee.myth.deamon.Deamon

2. 其他需要变化的参数
-managerHost 【managerIP地址】
-managerPort 【manager端口】
-bootstrapPort 【manager启动参数中的bootstrapPort值】
-osName 【平台名】
* */

    //主函数
    public static void main(String[] args) {
        if (args[0].compareTo("-version") == 0) {
            System.out.println("Deamon version : " + Version.getBuildVersion());
            return;
        }

        // initialize logger
        MLogger.init("deamon", LogConsts.LOGFLUSH_INTERVAL);

        // 从配置文件读取配置信息
        if (!Deamon.getInstance().parseArgs(args)) {
            return;
        }

        Deamon.getInstance().startOnlyOnce();
    }

    private String parseArg(String[] args, String param) {
        int argsNum = args.length;
        String retVal = null;
        for (int i=0; i<argsNum; i++) {
            if (args[i].compareTo(param)==0) {
                if (i+1<argsNum) {
                    retVal = args[i+1];
                }
                break;
            }
        }

        return retVal;
    }

    private boolean parseArgs(String[] args) {
        // parse manager host and port
        String managerHost = parseArg(args, "-managerHost");
        if (managerHost != null) {
            String managerPortStr = parseArg(args, "-managerPort");
            if (managerPortStr != null) {
                Integer managerPort = Integer.valueOf(managerPortStr);
                if (managerPort != null) {
                    managerAddress = new ServerAddress(managerHost, managerPort);
                } else {
                    System.err.println("Value of \"-managerPort\" param must be integer.");
                    return false;
                }
            } else {
                System.err.println("Lack \"-managerPort XXX\" param");
                return false;
            }
        } else {
            System.err.println("Lack \"-managerHost XXX\" param");
            return false;
        }

        // parse bootstrap port
        String bootstrapPortStr = parseArg(args, "-bootstrapPort");
        if (bootstrapPortStr != null) {
            Integer bootstrapPort = Integer.valueOf(bootstrapPortStr);
            if (bootstrapPort != null) {
                managerBootstrapPort = bootstrapPort;
            } else {
                System.err.println("Value of \"-bootstrapPort\" param must be integer.");
                return false;
            }
        } else {
            System.err.println("Lack \"-bootstrapPort XXX\" param");
            return false;
        }

        osName = parseArg(args, "-osName");
        if (osName == null) {
            System.err.println("Lack \"-osName XXX\" param");
            return false;
        } else {
            if (!(osName.toLowerCase().equals("linux") || osName.toLowerCase().equals("windows"))) {
                System.err.println("OS:"+osName+"not supported");
                return false;
            }
        }

        return true;
    }

    private void startHeartBeatTimer() {
        heartbeatTimer = new Timer();
        heartbeatTimer.schedule(new TimerTask() {

            @Override
            public void run() {
                ServerMessageQueue.queue().offer(DeamonMessageBuilder.buildHeartbeatMessage());
            }
        }, HEARTBEAT_PERIOD, HEARTBEAT_PERIOD);
    }

    public void startOnlyOnce() {
        // 连接manager server
        LengthFieldBasedFrameDecoder lengthFieldBasedFrameDecoder = new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 2, 4, 0, 0);
        DeamonToManagerEncoder type2BytesLengthFieldProtobufEncoder = new DeamonToManagerEncoder();
        DeamonToManagerHandler toManagerHandler = new DeamonToManagerHandler();
        deamonToManagerClient = DeamonToManagerClient.getInstance();
        deamonToManagerClient.init(managerAddress.getHost(), managerAddress.getPort(), lengthFieldBasedFrameDecoder, type2BytesLengthFieldProtobufEncoder, toManagerHandler);
        deamonToManagerClient.connectToServer();

        // 启动心跳Timer
        // TODO: 将Timer改成本地Timer
//            startHeartBeatTimer();

        // 启动消息处理线程
        new Thread(this).start();
    }

    @Override
    public void run() {
        while (true) {
            // handle message
            handleMessages();

            try {
                Thread.sleep(100);
            } catch (InterruptedException ex) {
                MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, StackTraceUtil.getStackTrace(ex));
            }
        }
    }

    private void handleMessages() {
        // TODO: handle message from message queue
        // 不处理shuttingDown过程中产生的消息
        Message msg = messageQueue.poll();  // 轮询消息队列

        while (msg != null) {
            switch (msg.getType()) {
                case ALL_HEARTBEAT:
                case DEAMON_MANAGERCONNECT:
                case DEAMON_MANAGERCLOSE:
                case DEAMON_MANAGERHEARTBEAT: {
                    // 处理Cluster Connect和Close消息
                    deamonToManagerClient.handleMessage(msg);
                    break;
                }
                case DEAMON_MANAGERDOWN: {
                    // TODO:
                    break;
                }
                case DEAMON_STARTSERVER: {
                    // 启动指定服务
                    StartServerDeamonMessage startServerDeamonMessage = (StartServerDeamonMessage) msg;
                    startService(startServerDeamonMessage.vmOptionStr, startServerDeamonMessage.paramStr);
                    break;
                }
                case DEAMON_SERVERCONFIGDATA: {
                    // 存储服务配置数据
                    ServerConfigDataDeamonMessage serverConfigDataDeamonMessage = (ServerConfigDataDeamonMessage) msg;
                    saveConfigData(serverConfigDataDeamonMessage.serverId, serverConfigDataDeamonMessage.data);
                    break;
                }
            }
            msg = messageQueue.poll();
        }
    }

    private void saveConfigData(int serverId, ByteString data) {
        try {
            // 创建server配置文件目录
            new File(String.valueOf(serverId)).mkdirs();

            ZipInputStream zis = new ZipInputStream(data.newInput());
            ZipEntry entry;
            while ((entry = zis.getNextEntry()) != null) {
                if (entry.isDirectory()) {
                    new File(serverId + "/" + entry.getName()).mkdirs();
                } else {
                    int count;
                    byte buf[] = new byte[2048];
                    // write the files to the disk
                    FileOutputStream fos = new FileOutputStream(serverId + "/" + entry.getName());
                    BufferedOutputStream dest = new BufferedOutputStream(fos, 2048);
                    while ((count = zis.read(buf, 0, 2048)) != -1) {
                        dest.write(buf, 0, count);
                    }
                    dest.flush();
                    dest.close();
                }
            }
        } catch (IOException ex) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, StackTraceUtil.getStackTrace(ex));
        }
    }

    private void startService(String vmOptionStr, String paramStr) {
        // TODO: 登记所启动的服务，以便跟踪管理
        try {
            System.out.println("Start");
            LinkedList<String> command = new LinkedList<String>();
            command.add("java");
            command.add("-Dfile.encoding=UTF-8");

            if (!vmOptionStr.trim().isEmpty()) {
                command.addAll(Arrays.asList(vmOptionStr.split(" ")));
            } else if (osName.toLowerCase().equals("linux")) {
                command.add("-Xms2g");
                command.add("-Xmx4g");
            }

            if (osName.toLowerCase().equals("linux")) {
                command.add("-Xbootclasspath/a:dist/ProjectKManagerDeamon.jar");
                command.add("-cp");
                command.add("\"$CLASSPATH;dist/lib/commons-codec-1.4.jar"+
                            ":dist/lib/mysql-connector-java-5.1.12-bin.jar"+
                            ":dist/lib/netty-3.2.3.Final.jar"+
                            ":dist/lib/gson-1.6.jar"+
                            ":dist/lib/commons-io-2.0.1.jar"+
                            ":dist/lib/commons-logging-1.1.1.jar"+
                            ":dist/lib/commons-jci-core-1.0.jar"+
                            ":dist/lib/protobuf-2.2.jar"+
                            ":dist/lib/httpclient-4.0.1.jar"+
                            ":dist/lib/httpcore-4.0.1.jar"+
                            ":dist/lib/oauth.jar"+
                            ":dist/lib/oauth-consumer.jar"+
                            ":dist/lib/oauth-httpclient4.jar"+
                            ":dist/lib/json-simple-1.1.jar"+
                            ":dist/ProjectKManagerDeamon.jar\"");
            } else if (osName.toLowerCase().equals("windows")) { // windows
                command.add("-XX:+ForceTimeHighResolution");    // 注意：调准时钟
                command.add("-cp");
                command.add("\"%CLASSPATH%;dist/lib/commons-codec-1.4.jar"+
                            ";dist/lib/mysql-connector-java-5.1.12-bin.jar"+
                            ";dist/lib/netty-3.2.3.Final.jar"+
                            ";dist/lib/gson-1.6.jar"+
                            ";dist/lib/commons-io-2.0.1.jar"+
                            ";dist/lib/commons-logging-1.1.1.jar"+
                            ";dist/lib/commons-jci-core-1.0.jar"+
                            ";dist/lib/protobuf-2.2.jar"+
                            ";dist/lib/httpclient-4.0.1.jar"+
                            ";dist/lib/httpcore-4.0.1.jar"+
                            ";dist/lib/oauth.jar"+
                            ";dist/lib/oauth-consumer.jar"+
                            ";dist/lib/oauth-httpclient4.jar"+
                            ";dist/lib/json-simple-1.1.jar"+
                            ";dist/ProjectKManagerDeamon.jar\"");
            }
            
            command.add("-Djava.system.class.loader=com.icee.myth.bootstrap.BootstrapClassLoader");
            command.add("-Dcom.icee.myth.manager.host=" + managerAddress.getHost());
            command.add("-Dcom.icee.myth.manager.port=" + managerBootstrapPort);
            command.add("com.icee.myth.server.GameServer");
            
            command.addAll(Arrays.asList(paramStr.split(" ")));

            String sCmd = command.toString();
            
            ProcessBuilder processBuilder = new ProcessBuilder(command);
            processBuilder.redirectErrorStream(true);
            Process process = processBuilder.start();
            new Thread(new ProcessOutputThread(process)).start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
